<?php
/* Autor: Lucas S. Beiler
   2⁰ semestre de Ciência da Computação ~ 22/08/2019
*/

// Recebe as requisições e as guarda em variáveis.
$usuario = $_POST['usuario'];
$senha = $_POST['senha'];
$confirmaSenha = $_POST['senha2'];
$ipLocal = $_POST['ipLocal'];

// Flags que podem ser facilmente alteradas por outros programadores a depender do ambiente em que forem usar o código.
$limitePorIPlocal = true;
$limitePorCookies = true;
$restringirAcessoPorOperadora = false;
$restringirAcessoPorIPExterno = true;
$hostnamesOperadorasPermitidas = array('gateway.univali.br', 'gvt.net.br');
$ipsExternosPermitidos = array('187.58.251.7');
$fimBloqueioLimite = time() + 7200;
date_default_timezone_set('America/Sao_Paulo');

// As variáveis abaixo devem SEMPRE ser inicializadas como false.
$problema = false;
$acessoSeletivoPermitido = false;
$arquivoListaUsuarios = 'lista.txt';
$arquivoIPsLocaisBloqueados = 'IPsLocais_bloqueados.txt';

if(empty($usuario) || empty($senha)){
  echo 'Requisição POST vazia e, portanto, ignorada. <br/><br/>';
  die();
}

// Verifica o hostname do IP externo do usuário para estabelecer o bloqueio por operadora quando necessário.
if($restringirAcessoPorOperadora){
  foreach($hostnamesOperadorasPermitidas as $hostname){
    if(strstr(gethostbyaddr($_SERVER['REMOTE_ADDR']), $hostname)){
      $acessoSeletivoPermitido = true;
    }
  }
}

if($restringirAcessoPorIPExterno){
  foreach($ipsExternosPermitidos as $ipExternoPermitido){
    if($_SERVER['REMOTE_ADDR'] == $ipExternoPermitido){
      $acessoSeletivoPermitido = true;
    }
  }
}

if(($restringirAcessoPorOperadora || $restringirAcessoPorIPExterno) && !$acessoSeletivoPermitido){
  echo 'Você não tem permissão para usar o sistema através desse IP ou operadora neste momento.';
  die();
}

// Verifica a existência do cookie de registro para verificar se o usuário não está abusando do sistema.
if($limitePorCookies && !empty($_COOKIE["registrado"])){
  echo 'ERRO B1: BLOQUEADO! Você já usou o site nas últimas duas horas. Volte às '. date('H:i:s', $_COOKIE["registrado"]) ."! <br/><br/>";
  die();
 }

 // Lê o arquivo que lista IPs locais bloqueados e timestamps de desbloqueio e aplica o bloqueio conforme necessário.
if($limitePorIPlocal){
  foreach(file($arquivoIPsLocaisBloqueados) as $valor){
    if(explode(':', $valor)[0] == $ipLocal && time() < explode(':', $valor)[1]){
      echo 'ERRO B2: BLOQUEADO! Você já usou o site nas últimas duas horas. Volte às '. date('H:i:s', explode(':', $valor)[1]) ."! <br/><br/>";
      die();
    }
  }
}

// Impede requisições contendo caracteres impróprios (contidos no array abaixo).
foreach(range('A', 'Z') as $letraMaiuscula){
  $caracteresProibidos = array(':', ' ', '\\', '/', ';', '|', '&', '$', '-', '=', '>', '<', '{', '}', '[', ']', '?', '#', '!', '@', '%', '+', '*', $letraMaiuscula);
  foreach($caracteresProibidos as $caracterProibido){
    if(strstr($usuario, $caracterProibido)){
      echo 'ERRO: Uso do caracter não permitido "', $caracterProibido,'" <br/><br/>';
      $problema = true;
    }
  }
}

// Impede números no início dos usernames
if(is_numeric($usuario[0])){
  echo 'ERRO: Nomes não devem começar com números!';
  $problema = true;
}

// Verifica se a confirmação da senha se iguala à senha original.
if($senha != $confirmaSenha){
  echo 'ERRO: A confirmação de senha não é igual à senha, tente outra vez! <br/><br/>';
  $problema = true;
}

// Verifica os nomes de usuário contidos na lista para impedir duplicatas.
foreach(file($arquivoListaUsuarios) as $valor){
  if($usuario == explode(":", $valor)[0]){ // Strings na lista estão na forma 'usuario:senha', explode() separa a string (":" como delimitador) em um array onde o nome de usuário fica no [0] e senha em [1].
    echo 'ERRO: O nome ', $usuario, ' já foi usado por outra pessoa! <br/><br/>';
    $problema = true;
  }
}

// Encerra a execução caso algum problema tenha ocorrido em algum dos passos anteriores.
if($problema){
  die();
}else{

while(1){
  $saltSenha = bin2hex(openssl_random_pseudo_bytes(32)); // Gera um salt relativamente grande e aleatório para a hash de senha a ser gerada.
  $senhaEncriptada = crypt($senha, sprintf('$6$rounds=%d$%s$', 6000, $saltSenha));
  if(!strstr($senhaEncriptada, ':')){ // Quebra o loop apenas quando a hash gerada não contém nenhum ':', ele é o delimitador de usuarios:senhas e não queremos ele em outras partes da lista.
    break;
  }
}

// Salva os dados recebidos em um arquivo e marca um cookie no navegador do usuário com o timestamp do registro como valor.
if(file_put_contents($arquivoListaUsuarios, "$usuario:'$senhaEncriptada'\n", FILE_APPEND)){
  if(empty($_COOKIE["registrado"]) && $limitePorCookies){
    setcookie("registrado", $fimBloqueioLimite, $fimBloqueioLimite);
  }if($limitePorIPlocal && !empty($ipLocal)){
    file_put_contents($arquivoIPsLocaisBloqueados, "$ipLocal:$fimBloqueioLimite\n", FILE_APPEND);
  }
  echo 'Concluído, os dados foram salvos com sucesso!';
  die();
}
}
?>
